"""
    类五子棋的ai
    支持不同难度的选择
    使用Arisu价值图和Arisu价值图博弈树算法
    使用价值图进行胜利预估
    帮助任意角色进行落子
"""
import multiprocessing
import random
import threading
from datetime import datetime


# 简单的ai落子策略(直接判断当前位置的价值和,落于价值最大的位置) 将在子进程中进行
def do_ai_chess_easy(white_value_map: list, black_value_map: list, do_chess_role: int,
                     output_queue: multiprocessing.Queue):
    print("执行子进程ai落子策略计算(普通)...")
    start_time = datetime.now()
    max_value = 0
    value_array = []
    for i in range(len(white_value_map)):
        for y in range(len(white_value_map[0])):
            if black_value_map[i][y] < 0: continue
            if black_value_map[i][y] == 0 and white_value_map[i][y] == 0:
                if max_value == 0: value_array.append((i, y))
                continue
            now_value = ChessAI.get_sum_value(black_value_map[i][y], role=-1) + ChessAI.get_sum_value(white_value_map[i][y], role=1)
            if now_value < max_value: continue
            if now_value > max_value:
                max_value = now_value
                value_array = []
            value_array.append((i, y))
    print(f"ai落子策略计算耗时: {(datetime.now() - start_time).total_seconds()}秒")
    # 从解集中取最优解
    output_queue.put(random.choice(value_array))

# 普通的ai落子策略(模拟落子后判断产生的价值,落于能产生最大价值的位置) 将在子进程中进行
def do_ai_chess_normal(white_value_map: list, black_value_map: list, do_chess_role: int,
                     output_queue: multiprocessing.Queue):
    print("执行子进程ai落子策略计算(正常)...")
    def imitate_do_chess(i_white_value_map, i_black_value_map, direct_list,point_x:int,point_y:int,role:int) -> int:
        aim_map = i_white_value_map if role == 1 else i_black_value_map
        dir_tuple = direct_list[0]  # 获取侦察位移偏量
        direct = direct_list[1]  # 获取方向
        clicked_value = ChessAI.get_dir_value(aim_map[point_y][point_x], direct)
        point_x += dir_tuple[0]
        point_y += dir_tuple[1]
        # 删除越界项目
        if (0 > point_x or point_x >= len(i_white_value_map[0])) or (0 > point_y or point_y >= len(i_white_value_map)):  return 0
        # 追加价值
        now_value = aim_map[point_y][point_x]
        if now_value > -1:  return (ChessAI.value_list_white if role == 1 else ChessAI.value_list_black)[ChessAI.get_dir_value(aim_map[point_y][point_x], direct)] - (ChessAI.value_list_white if role == 1 else ChessAI.value_list_black)[clicked_value + 1]
        else:
            while now_value == -1:  # 存在己方棋子,则继续前进,在己方棋子结束处进行追加
                point_y += dir_tuple[1]
                point_x += dir_tuple[0]
                now_value = aim_map[point_y][point_x]
            if now_value == -3:  return 0  # 如果是对方的棋子,则返回
            return (ChessAI.value_list_white if role == 1 else ChessAI.value_list_black)[clicked_value + 1] - (ChessAI.value_list_white if role == 1 else ChessAI.value_list_black)[ChessAI.get_dir_value(aim_map[point_y][point_x], direct)]
    start_time = datetime.now()
    max_value = 0
    value_array = []
    for i in range(len(white_value_map)):  # 对每个点位都进行模拟落子
        for y in range(len(white_value_map[0])):
            now_all_value_w = ChessAI.get_sum_value(white_value_map[i][y], do_chess_role)
            now_all_value_b = ChessAI.get_sum_value(black_value_map[i][y], do_chess_role)
            each_sum_value_sum = 0
            for aim_direct in ChessAI.DIRECT_DICT.values():
                each_sum_value_sum += imitate_do_chess(white_value_map, black_value_map, aim_direct, i, y, do_chess_role)
            now_value_sum = each_sum_value_sum + (now_all_value_b if do_chess_role == 1 else now_all_value_w) - (now_all_value_w if do_chess_role == 1 else now_all_value_b)
            if now_value_sum < max_value: continue
            if now_value_sum > max_value:
                max_value = now_value_sum
                value_array = []
            value_array.append((i, y))
    print(f"ai落子策略计算耗时: {(datetime.now() - start_time).total_seconds()}秒")
    # 从解集中取最优解
    output_queue.put(random.choice(value_array))


class ChessAI:
    # ai难度
    EASY_DIFFICULTY = 0
    NORMAL_DIFFICULTY = 1

    # 价值对照表
    value_list_white = [0, 11, 30, 200, 2000, 6000, 12000]
    value_list_black = [0, 8, 25, 150, 1800, 5700, 10000]

    # 快速位运算列表
    DIRECTION_MOVE = [0, 24, 16, 8, 0]
    MASK_BITS = [0x00000000, 0xFF000000, 0x00FF0000, 0x0000FF00, 0x000000FF]
    CLEAR_MASK = [0x00000000, 0x00FFFFFF, 0xFF00FFFF, 0xFFFF00FF, 0xFFFFFF00]

    # {“水平”:1,“垂直”:2,“左向右下对角线”:3,“右向左下对角线”:4}
    DICT_DIR = {"vertical": 1, "horizontal": 2, "left diagonal": 3, "right diagonal": 4}
    DIRECT_DICT = {"left_top": [(-1,-1),3], "top": [(0,-1),1], "right_top":[(1,-1),4],"left":[(-1,0),2],"right":[(1,0),2],"left_bottom":[(-1,1),4],"bottom":[(0,1),1],"right_bottom":[(1,1),3]}

    DIFFICULTY_MAPPER = [do_ai_chess_easy, do_ai_chess_normal]  #     EASY_DIFFICULTY = 0  NORMAL_DIFFICULTY = 1

    def __init__(self, model, difficulty_type=0):
        self.model = model
        self.height_num = model.height_num
        self.width_num = model.width_num
        self.difficulty_type = difficulty_type
        self.consumer_process = None
        self.get_ai_strategy_thread = None
        self.stop_get_ai_strategy_thread = False
        # 适配ai落子策略
        self.do_ai_chess_by_difficulty = ChessAI.DIFFICULTY_MAPPER[self.difficulty_type]
        self.link_ai_queue = multiprocessing.Queue()  # 用于和ai决策进程进行通讯而使用的队列
        self.ai_do_chess_pos = None
        # 维护的价值图
        self.white_value_map = [[0 for __ in range(self.height_num)] for _ in range(self.width_num)]
        self.black_value_map = [[0 for __ in range(self.height_num)] for _ in range(self.width_num)]

    def reset_ai(self, model, difficulty_type=0):
        self.model = model
        self.height_num = model.height
        self.width_num = model.width
        self.difficulty_type = difficulty_type
        # 适配ai落子策略
        self.do_ai_chess_by_difficulty = ChessAI.DIFFICULTY_MAPPER[self.difficulty_type]
        self.link_ai_queue = multiprocessing.Queue()  # 用于和ai决策进程进行通讯而使用的队列
        # 维护的价值图
        self.white_value_map = [[0 for __ in range(self.height_num)] for _ in range(self.width_num)]
        self.black_value_map = [[0 for __ in range(self.height_num)] for _ in range(self.width_num)]

    def reset(self):
        # 维护的价值图
        self.white_value_map = [[0 for __ in range(self.height_num)] for _ in range(self.width_num)]
        self.black_value_map = [[0 for __ in range(self.height_num)] for _ in range(self.width_num)]

    def show_value_map(self):
        print(
            "白棋价值图:                                             黑棋价值图:")
        for h in range(self.height_num):
            for w in range(self.width_num):
                value_white = self.white_value_map[h][w]
                print(""if value_white<0 else " ",self.max_dir_value(value_white), end="")
            print("         ",end="")
            for w in range(self.width_num):
                value_black = self.black_value_map[h][w]
                print(""if value_black<0 else " ",self.max_dir_value(value_black), end="")
            print()

    def _update_value_map(self, point_x, point_y,role):
        # 通过点位更新价值图 WHITE_CHESS = 1
        print("help")

        for aim_direct in ChessAI.DIRECT_DICT.values():
            self._update_value_map_help(aim_direct, point_x, point_y, role)
        # 赋值
        self.white_value_map[point_y][point_x] = -3 if role != 1 else -1
        self.black_value_map[point_y][point_x] = -1 if role != 1 else -3

    def _update_value_map_help(self,direct_list: list,point_x: int, point_y: int, role: int):
        # 核心的更新价值图方法
        aim_map = self.white_value_map if role == 1 else self.black_value_map
        dir_tuple = direct_list[0]  # 获取侦察位移偏量
        direct = direct_list[1]  # 获取方向
        clicked_value = ChessAI.get_dir_value(aim_map[point_y][point_x],direct)
        point_x += dir_tuple[0]
        point_y += dir_tuple[1]
        # 删除越界项目
        if (0 > point_x or point_x >= self.width_num) or (0 > point_y or point_y >= self.height_num):  return

        # 追加价值
        now_value = aim_map[point_y][point_x]
        if now_value > -1:
            aim_map[point_y][point_x] = ChessAI.add_value(now_value, clicked_value + 1, direct)
        else:
            while now_value == -1:  # 存在己方棋子,则继续前进,在己方棋子结束处进行追加
                point_y += dir_tuple[1]
                point_x += dir_tuple[0]
                if (0 > point_x or point_x >= self.width_num) or (0 > point_y or point_y >= self.height_num):  return
                now_value = aim_map[point_y][point_x]
            if now_value == -3:  return  # 如果是对方的棋子,则返回
            aim_map[point_y][point_x] = ChessAI.set_value(now_value, clicked_value + 1, direct)

    def update_value_map(self, point_x, point_y) -> None:
        # 传达落子命令,更新价值图
        self._update_value_map(point_x, point_y, self.model.legal_role)  # 角色

    def do_ai_chess(self):
        # 根据不同的难度选择不同的算法进行ai落子
        # 在子进程中进行ai落子(加快运行速度并且防止页面发生卡顿)***
        self.consumer_process = multiprocessing.Process(target=self.do_ai_chess_by_difficulty, args=(self.white_value_map, self.black_value_map, self.model.legal_role,self.link_ai_queue))
        self.consumer_process.start()
        #  在子线程中获取ai的策略落子点(state: 1:执行落子 2:给用户提示落子位置)
        self.get_ai_strategy_thread = threading.Thread(target=self.get_ai_strategy_help)
        self.get_ai_strategy_thread.start()

    def get_ai_strategy_help(self):
        # 获取AI的落子位置
        print("开始执行获取子进程消息的线程...")
        pos = self.link_ai_queue.get()
        if pos is None: return
        self.ai_do_chess_pos = pos  # 假设这是类的一个属性
        print(f"AI的落子位置是: {pos}")

    def close(self):
        if self.consumer_process and self.consumer_process.is_alive(): self.consumer_process.close()
        if self.get_ai_strategy_thread and self.get_ai_strategy_thread.is_alive(): self.link_ai_queue.put(None)

    @staticmethod
    def set_value(value: int, aim_value: int, direct: int) -> int:
        # 设置某个方向的值并返回    方向定义：1: 横, 2: 竖, 3: \左斜向右, 4: /右斜向左
        if value < 0: return value
        shift = ChessAI.DIRECTION_MOVE[direct]
        i = (value & ChessAI.MASK_BITS[direct]) >> shift  # 提取当前位的值
        if aim_value == -1:  i = (i + 1) % 256
        else:  i = aim_value
        value &= ChessAI.CLEAR_MASK[direct]
        value |= (i << shift)
        return value

    @staticmethod
    def add_value(value: int, added_value: int, direct:int):
        # 添加某个方向的值并返回    方向定义：1: 横, 2: 竖, 3: \左斜向右, 4: /右斜向左
        if value < 0: return value
        shift = ChessAI.DIRECTION_MOVE[direct]
        i = (value & ChessAI.MASK_BITS[direct]) >> shift  # 提取当前位的值
        value &= ChessAI.CLEAR_MASK[direct]
        value |= ((i + added_value) << shift)
        return value

    @staticmethod
    def get_dir_value(value: int, direct: int) -> int:
        # 获取某个方向上的价值 {“垂直”:1,“水平”:1,“左向右下对角线”:3,“右向左下对角线”:4}
        if value < 0:  return value
        return (value & ChessAI.MASK_BITS[direct]) >> ChessAI.DIRECTION_MOVE[direct] # 根据dir的值提取相应的字节

    @staticmethod
    def get_each_values(value: int) -> list:
        # 获取每个方向的值并装在数组中返回
        if value < 0: return [0, 0, 0, 0]
        return [(value & 0xFF000000) >> 24,(value & 0x00FF0000) >> 16,(value & 0x0000FF00) >> 8,value & 0x000000FF]

    @staticmethod
    def max_dir_value(value: int) -> int:
        # 获取所有方向中最大的值
        if value < 0: return value
        max_val = max((value & 0xFF000000) >> 24,(value & 0x00FF0000) >> 16,(value & 0x0000FF00) >> 8,value & 0x000000FF)
        return max_val

    @staticmethod
    def get_sum_value(value: int, role: int) -> int:
        # 根据字典获取该点的价值和
        if value < 0: return 0
        aim_value_map = ChessAI.value_list_white if role == 1 else ChessAI.value_list_black
        return (aim_value_map[ChessAI.get_dir_value(value,1)] + aim_value_map[ChessAI.get_dir_value(value,2)]
                + aim_value_map[ChessAI.get_dir_value(value,3)] + aim_value_map[ChessAI.get_dir_value(value,4)])
        # return aim_value_map[ChessAI.get_dir_value(value,1)] + aim_value_map[ChessAI.get_dir_value(value,2)] + aim_value_map[ChessAI.get_dir_value(value,3)] + aim_value_map[ChessAI.get_dir_value(value,4)]